home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Devices / AsyncDriverSample1.0b4 / TradDriverLoaderLib / TradDriverLoaderLib.c next >
Encoding:
C/C++ Source or Header  |  1997-05-21  |  22.1 KB  |  775 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TradDriverLoaderLib.c
  3.  
  4.     Contains:    Implementation for the pseudo-DriverLoaderLib for 'DRVR's.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. #include <LowMem.h>
  22. #include <DriverGestalt.h>
  23. #include <TextUtils.h>
  24.  
  25. // Switched from using:
  26. //
  27. //   #include <PLStringFuncs.h>
  28. //
  29. // to using BlockMoveData because it's so hard to get PLstrcpy working
  30. // across a zillion different compilers.  *sigh*
  31.  
  32. #include "TradDriverLoaderLib.h"
  33.  
  34. ///////////////////////////////////////////////////////////////////////////
  35.  
  36. extern pascal SInt16 TradHigherDriverVersion(NumVersion *dv1, NumVersion *dv2)
  37. {
  38.     UInt16 nonRelRev1, nonRelRev2;
  39.  
  40.     if (dv1->majorRev           > dv2->majorRev)        return  1;
  41.     if (dv1->majorRev           < dv2->majorRev)        return -1;
  42.     if (dv1->minorAndBugRev     > dv2->minorAndBugRev)  return  1;
  43.     if (dv1->minorAndBugRev     < dv2->minorAndBugRev)  return -1;
  44.     if (dv1->stage              > dv2->stage)           return  1;
  45.     if (dv1->stage              < dv2->stage)           return -1;
  46.  
  47.     nonRelRev1 = dv1->nonRelRev;
  48.     nonRelRev2 = dv2->nonRelRev;
  49.     
  50.     if (dv1->stage == finalStage) {
  51.         if (dv1->nonRelRev == 0)                 nonRelRev1 = 0xFFFF;
  52.         if (dv2->nonRelRev == 0)                 nonRelRev2 = 0xFFFF;
  53.     }
  54.  
  55.     if (nonRelRev1 > nonRelRev2)                        return  1;
  56.     if (nonRelRev1 < nonRelRev2)                        return -1;
  57.  
  58.     return 0;
  59. }
  60.  
  61.  
  62. ///////////////////////////////////////////////////////////////////////////
  63.  
  64. extern pascal UnitNumber TradHighestUnitNumber(void)
  65.     // See comment in header file.
  66. {
  67.     return ( LMGetUnitTableEntryCount() - 1);
  68. }
  69.  
  70. ///////////////////////////////////////////////////////////////////////////
  71.  
  72. extern pascal Boolean TradDriverGestaltIsOn(DriverFlags flags)
  73.     // See comment in header file.
  74. {
  75.     return ( (flags & kmDriverGestaltEnableMask) != 0 );
  76. }
  77.  
  78. ///////////////////////////////////////////////////////////////////////////
  79.  
  80. static OSErr DriverGestaltOnOff(DriverRefNum refNum, Boolean setIt)
  81.     // This routine is called by TradDriverGestaltOn and
  82.     //  TradDriverGestaltOff to either set or clear the
  83.     //  kmDriverGestaltEnableMask bit in the DCE flags.
  84. {
  85.     OSErr err;
  86.     AuxDCEHandle thisDCE;
  87.     
  88.     // First called TradGetDriverInformation to validate the refNum
  89.     //  and verify that the driver exists.
  90.     err = TradGetDriverInformation(refNum, nil, nil, nil, nil);
  91.     if (err == noErr) {
  92.         thisDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  93.         if (setIt) {
  94.             (**thisDCE).dCtlFlags |= kmDriverGestaltEnableMask;
  95.         } else {
  96.             (**thisDCE).dCtlFlags &= ~kmDriverGestaltEnableMask;
  97.         }
  98.     }
  99.     
  100.     return (err);
  101. }
  102.  
  103. ///////////////////////////////////////////////////////////////////////////
  104.  
  105. extern pascal OSErr TradDriverGestaltOn(DriverRefNum refNum)
  106.     // See comment in header file.
  107. {
  108.     return ( DriverGestaltOnOff(refNum, true) );
  109. }
  110.  
  111. ///////////////////////////////////////////////////////////////////////////
  112.  
  113. extern pascal OSErr TradDriverGestaltOff(DriverRefNum refNum)
  114.     // See comment in header file.
  115. {
  116.     return ( DriverGestaltOnOff(refNum, false) );
  117. }
  118.  
  119. ///////////////////////////////////////////////////////////////////////////
  120.  
  121. extern pascal OSErr TradOpenInstalledDriver(DriverRefNum refNum, SInt8 ioPermission)
  122.     // See comment in header file.
  123. {
  124.     OSErr                 err;
  125.     Str255                driverName;
  126.     DriverRefNum    realRefNum;
  127.  
  128.     // Check parameters.
  129.     err = noErr;
  130.     if (ioPermission != fsRdWrPerm) {
  131.         err = paramErr;
  132.     }
  133.     
  134.     // Get the name of the driver, then simply open it.
  135.     if (err == noErr) {
  136.         err = TradGetDriverInformation(refNum, nil, nil, driverName, nil);
  137.     }
  138.     if (err == noErr) {
  139.         if ( driverName[0] == 0 ) {
  140.             err = paramErr;
  141.         }
  142.     }
  143.     if (err == noErr) {
  144.         err = OpenDriver(driverName, &realRefNum);
  145.     }
  146.     if (err == noErr) {
  147.         if (realRefNum != refNum) {
  148.             err = paramErr;        // My favourite error code -- at some intrinsic level, every error is a paramErr (-;
  149.         }
  150.     }
  151.     
  152.     return (err);
  153. }
  154.  
  155. ///////////////////////////////////////////////////////////////////////////
  156.  
  157. extern pascal OSErr TradLookupDrivers(UnitNumber beginningUnit,
  158.                                         UnitNumber endingUnit,
  159.                                         Boolean emptyUnits,
  160.                                         ItemCount *returnedRefNums, 
  161.                                         DriverRefNum *refNums)
  162.     // See comment in header file.
  163. {
  164.     OSErr err;
  165.     AuxDCEHandle     *unitTable;
  166.     ItemCount         maxRefNums;
  167.     UnitNumber         currentUnit;
  168.     
  169.     // Sanity check the parameters.
  170.     if ( endingUnit > TradHighestUnitNumber() ) {
  171.         endingUnit = TradHighestUnitNumber();
  172.     }
  173.     err = noErr;
  174.     if ( beginningUnit > TradHighestUnitNumber() ) {
  175.         err = badUnitErr;
  176.     }
  177.     if (err == noErr) {
  178.         if (beginningUnit > endingUnit ) {
  179.             err = paramErr;
  180.         }
  181.     }
  182.  
  183.     // Now do the real work...
  184.     if (err == noErr) {
  185.         unitTable = (AuxDCEHandle *) LMGetUTableBase();
  186.  
  187.         maxRefNums = *returnedRefNums;
  188.         
  189.         // Loop through each unit table entry from beginningUnit to endingUnit inclusive.
  190.         *returnedRefNums = 0;
  191.         currentUnit = beginningUnit;
  192.         while ( currentUnit <= endingUnit ) {
  193.  
  194.             // If we've still got space to return a unit...
  195.             if ( *returnedRefNums < maxRefNums ) {
  196.             
  197.                 // and we're interested in this unit...
  198.                 if (    (emptyUnits && unitTable[currentUnit] == nil) ||
  199.                             (!emptyUnits && unitTable[currentUnit] != nil) ) {
  200.                     
  201.                     // then copy the unit out to the caller's array
  202.                     refNums[*returnedRefNums] = ~currentUnit;
  203.                     *returnedRefNums += 1;
  204.                 }
  205.             }
  206.             currentUnit += 1;
  207.         }
  208.     
  209.     }
  210.     
  211.     return (err);
  212. }
  213.  
  214. ///////////////////////////////////////////////////////////////////////////
  215.  
  216. enum {
  217.     kNoUnitNumber = 0xFFFF
  218. };
  219.  
  220. static UnitNumber IsDriverInstalled(ConstStr255Param name, UnitNumber skipThisUnit)
  221.     // Look through the unit table to see if there is a driver with this name
  222.     //  already installed.  Note that you might consider calling OpenDriver
  223.     //  here, but that would be wrong.  OpenDriver has similar semantics, but
  224.     //  if it fails to find a driver in the unit table it will search the
  225.     //  current resource chain looking for a DRVR resource to install.
  226.     //  Given that it's likely our client has a DRVR resource in their
  227.     //  resource chain ('cause they're messing around trying to install
  228.     //  drivers), and that OpenDriver will install it without detaching
  229.     //  it from the client's resource file, and that the client's
  230.     //  resource file may go away (ie they're a DropMounter-like application
  231.     //  or some INIT running at system startup), this would be bad.
  232. {
  233.     UnitNumber    endingUnit;
  234.     UnitNumber    unit;
  235.     Str255            unitName;
  236.     
  237.     endingUnit = TradHighestUnitNumber();
  238.     
  239.     for (unit = 0; unit <= endingUnit; unit++) {
  240.         if ( TradGetDriverInformation(~unit, nil, nil, unitName, nil) == noErr) {
  241.             if ( unit != skipThisUnit && EqualString(name, unitName, false, true) ) {
  242.                 return (unit);
  243.             }
  244.         }
  245.     }
  246.     
  247.     return (kNoUnitNumber);
  248. }
  249.  
  250. ///////////////////////////////////////////////////////////////////////////
  251.  
  252. enum {
  253.     kMaximumNumberOfUnitTableEntries = 128,
  254.     // kMaximumNumberOfUnitTableEntries = 8192,
  255.     
  256.     // kMaximumNumberOfUnitTableEntries is documented in Technote
  257.     //  DV 23 "Driver Education" <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  258.     //  as being the maximum size that the classic Device Manager
  259.     //  would grow the unit table.  In theory, this limits the system
  260.     //  to 128 unit table entries.  This limit is also enforced by the
  261.     //  PCI DriverLoaderLib.
  262.     // However the traditional Mac OS is capable of dealing with much more
  263.     //  than 128 units.  In fact, some multi-port serial card vendors
  264.     //  regularly install more.  So while I've set this limit to 128,
  265.     //  I have tested TradDriverLoaderLib installing up to 500 device
  266.     //  drivers.  I've also filed a bug against the PCI DriverLoaderLib to
  267.     //  get its limit raised.
  268.     // The alternative maximum I've supplied (8192) is designed to
  269.     //  keep the unit table smaller than 32K.  This is important
  270.     //  because many people use 68K word indexing (ie x(a0,d0.w) to
  271.     //  to access the entries.
  272.     
  273.     kNumberOfEntriesToGrowUnitTable = 4
  274.     
  275.     // Technote DV 23 "Driver Education"
  276.     //  <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  277.     //  documents that the system grows the unit table by 4 entries
  278.     //  at a time.
  279. };
  280.  
  281. ///////////////////////////////////////////////////////////////////////////
  282.  
  283. static OSErr GrowUnitTable()
  284.     // This routine grows the unit table by kNumberOfEntriesToGrowUnitTable,
  285.     //  up to a maximum of kMaximumNumberOfUnitTableEntries.  The routine
  286.     //  is guaranteed to grow the table by at least one entry, or fail
  287.     //  with an error.
  288. {
  289.     OSErr        err;
  290.     Ptr         oldTable;
  291.     Ptr         newTable;
  292.     UInt32    oldCount;
  293.     UInt32    newCount;
  294.  
  295.     // Get the info about the old table, and calculate the new table size.    
  296.     oldTable = LMGetUTableBase();
  297.     oldCount = LMGetUnitTableEntryCount();
  298.     newCount = oldCount + kNumberOfEntriesToGrowUnitTable;
  299.  
  300.     // Guard against growing the table too big.    
  301.     err = noErr;
  302.     if (newCount > kMaximumNumberOfUnitTableEntries) {
  303.         err = unitTblFullErr;
  304.     }
  305.     
  306.     // Allocate the new unit table in the system heap.  Note that we
  307.     //  clear the newly allocated memory, so that later on, when we
  308.     //  use this memory as the new unit table, the newly allocated
  309.     //  entries will be empty.
  310.  
  311.     if (err == noErr) {
  312.         newTable = NewPtrSysClear( newCount * sizeof(AuxDCEHandle));
  313.         err = MemError();
  314.     }
  315.  
  316.     // Copy the unit table entries over to the new table and then switch
  317.     //  to that table.  Note that this sequence doesn't disable interrupts,
  318.     //  instead it relies on the fact that programs can't modify the
  319.     //  unit table at interrupt time, and thus we, running at non-interrupt
  320.     //  time, have exclusive write access to the table.
  321.  
  322.     // Note that the sequence of these next few lines is *very* important.
  323.     //  If we did this in any other order, you could get to a situation
  324.     //  where interrupt code might be looking at an inconsistent 
  325.     //  unit table, which would be bad.
  326.     
  327.     // The sequence is:
  328.     //  1. copy the old unit table entries to the new table
  329.     //  2. change the unit table base pointer, so that interrupts
  330.     //         start using the new unit table
  331.     //  3. then change the unit table count, so that we have
  332.     //         more entries available
  333.     
  334.     if (err == noErr) {
  335.         BlockMoveData(oldTable, newTable, oldCount * sizeof(AuxDCEHandle));    // 1.
  336.         LMSetUTableBase(newTable);                                                                                    // 2.
  337.         LMSetUnitTableEntryCount(newCount);                                                                    // 3.
  338.         
  339.         // Now its safe to dispose of the old unit table.
  340.         DisposePtr(oldTable);
  341.     }
  342.  
  343.     return (err);    
  344. }
  345.  
  346. ///////////////////////////////////////////////////////////////////////////
  347.  
  348. static OSErr FindFreeUnitNumber(UnitNumber beginningUnit,
  349.                                 UnitNumber endingUnit, 
  350.                                 UnitNumber *foundUnit)
  351.     // This routine walks the unit table looking for a free
  352.     //  slot.  The slot must be between beginningUnit
  353.     //  and endingUnit.  If endingUnit is greater than
  354.     //  TradHighestUnitNumber(), then we're allowed
  355.     //  to grow the unit table to meet our needs.
  356. {
  357.     OSErr err;
  358.     Boolean found;
  359.     UnitNumber currentUnit;
  360.     UnitNumber trueEndingUnit;
  361.     AuxDCEHandle *unitTable;
  362.     
  363.     unitTable = (AuxDCEHandle *) LMGetUTableBase();
  364.     
  365.     // Find trueEndingUnit, which is the minimum
  366.     //  of endingUnit and the highest unit number.
  367.     trueEndingUnit = endingUnit;
  368.     if ( trueEndingUnit > TradHighestUnitNumber() ) {
  369.         trueEndingUnit = TradHighestUnitNumber();
  370.     }
  371.  
  372.     // Scan through the unit table, starting at beginningUnit
  373.     //  and ending at trueEndingUnit, looking for an
  374.     //  empty slot.
  375.     currentUnit = beginningUnit;
  376.     found = false;
  377.     while (currentUnit <= trueEndingUnit && !found) {
  378.         found = (unitTable[currentUnit] == nil);
  379.         if (!found) {
  380.             currentUnit += 1;
  381.         }
  382.     }
  383.  
  384.     // Finish up.    
  385.     if (found) {
  386.         // We found an empty slot, return it.
  387.         *foundUnit = currentUnit;
  388.         err = noErr;
  389.     } else {
  390.  
  391.         // We didn't find an empty slot.  If we're
  392.         //  allowed to, grow the unit table, otherwise
  393.         //  just return an error.
  394.         
  395.         if (endingUnit > trueEndingUnit) {
  396.             err = GrowUnitTable();
  397.             if (err == noErr) {
  398.                 *foundUnit = trueEndingUnit + 1;
  399.             }
  400.         } else {
  401.             err = unitTblFullErr;
  402.         }
  403.     }
  404.     
  405.     return (err);    
  406. }
  407.  
  408. ///////////////////////////////////////////////////////////////////////////
  409.  
  410. extern pascal OSErr TradInstallDriverFromPtr(DRVRHeaderPtr driver,
  411.                                                 UnitNumber beginningUnit,
  412.                                                 UnitNumber endingUnit,
  413.                                                 DriverRefNum *refNum)
  414.     // See comment in header file.
  415. {
  416.     OSErr err;
  417.     UnitNumber foundUnit;
  418.     AuxDCEHandle theDCE;
  419.     
  420.     // Sanity check parameters.
  421.     err = noErr;
  422.     if ( driver == nil ) {
  423.         err = paramErr;
  424.     }
  425.     if ( beginningUnit > TradHighestUnitNumber() ) {
  426.         err = badUnitErr;
  427.     }
  428.     if ( err == noErr && beginningUnit > endingUnit ) {
  429.         err = paramErr;
  430.     }
  431.     
  432.     // Check whether this driver is already installed.
  433.     if ( err == noErr ) {
  434.         // Check whether it's already installed.
  435.         foundUnit = IsDriverInstalled(&driver->drvrName[0], kNoUnitNumber);
  436.         if (foundUnit != kNoUnitNumber) {
  437.             // Return the refnum of the existing driver to the caller.
  438.             *refNum = ~foundUnit;
  439.             err = dupFNErr;
  440.         }
  441.     }
  442.     
  443.     // Now walk the unit table looking for a free slot.
  444.     if (err == noErr) {
  445.         err = FindFreeUnitNumber(beginningUnit, endingUnit, &foundUnit);
  446.     }
  447.  
  448.     // We've got a free slot, so let's install the device driver.
  449.     //  Note that we use DriverInstallReserveMem, rather than the standard
  450.     //  DriverInstall, so that the DCE is allocated low in the system
  451.     //  heap.  DriverInstallReserveMem was introduced with the 128K ROM.
  452.  
  453.     if (err == noErr) {
  454.         err = DriverInstallReserveMem(driver, ~foundUnit);
  455.     }
  456.     
  457.     // Now do some important tidying up.
  458.     if (err == noErr) {
  459.  
  460.         // Return the refNum to the caller.
  461.         *refNum = ~foundUnit;
  462.  
  463.         theDCE = (AuxDCEHandle) GetDCtlEntry(*refNum);
  464.         
  465.         // Now setup the DCE properly.  There's a whole pile of things we
  466.         //  have to do, mainly because DriverInstall is such a brain-dead
  467.         //  routine.
  468.         
  469.         // First up, DriverInstall seems to ignore the first parameter
  470.         //  passed to it, so we have to blat the pointer to the driver code in
  471.         //  yourself afterwards.
  472.         
  473.         (**theDCE).dCtlDriver = (Ptr) driver;
  474.  
  475.         // Then we have to set up the flags.  We do this by copying the flags
  476.         //  out of the first word of the driver code.  We make sure to clear
  477.         //  the dRAMBased bit because we're actually a pointer-based driver
  478.         //  and DriverInstallReserveMem sets it to provisionally indicate that
  479.         //  we're a handle based driver.  We also set dNeedLock because
  480.         //  we want the the Device Manager to lock down the DCE.
  481.         
  482.         (**theDCE).dCtlFlags = (driver->drvrFlags & ~dRAMBasedMask) | dNeedLockMask;
  483.  
  484.         // There's also a bunch of fields we copy straight across without
  485.         //  any modification.  You might expect DriverInstall to copy
  486.         //  across these fields from the driver header to the DCE, but it doesn't
  487.         //  do that, so we do it ourselves.
  488.  
  489.         (**theDCE).dCtlDelay = driver->drvrDelay;
  490.         (**theDCE).dCtlEMask = driver->drvrEMask;
  491.         (**theDCE).dCtlMenu  = driver->drvrMenu;
  492.  
  493.         // Finally, we lock the DCE.
  494.         // Note that strictly speaking we don't need to HLock the DCE
  495.         //  because the Device Manager will do it when it you open a driver
  496.         //  that has dNeedLock set.  However, we want to
  497.         //  lock it now because DriverInstallReserveMem has just made sure
  498.         //  that the DCE was created low in the system heap, so we might as
  499.         //  well lock it down low rather than let it float.
  500.  
  501.         HLock( (Handle) theDCE );
  502.     }
  503.     
  504.     return (err);
  505. }
  506.  
  507. ///////////////////////////////////////////////////////////////////////////
  508.  
  509. extern pascal OSErr TradInstallDriverFromHandle(DRVRHeaderHandle driver,
  510.                                                 UnitNumber beginningUnit,
  511.                                                 UnitNumber endingUnit,
  512.                                                 DriverRefNum *refNum)
  513.     // See comment in header file.
  514. {
  515.     OSErr err;
  516.     Size  driverSize;
  517.     DRVRHeaderPtr driverPtr;
  518.     
  519.     driverPtr = nil;
  520.     
  521.     err = noErr;
  522.     if (driver == nil || *driver == nil) {
  523.         err = paramErr;
  524.     }
  525.     if (err == noErr) {
  526.         driverSize = GetHandleSize( (Handle) driver );
  527.     }
  528.     if (err == noErr) {
  529.         driverPtr = (DRVRHeaderPtr) NewPtrSys( driverSize );
  530.         err = MemError();
  531.     }
  532.     
  533.     if (err == noErr) {
  534.         // This is *not* a BlockMoveData call. This time, we really are moving code!
  535.         //  I could have put cache flushing code in here, but then I would have
  536.         //  had to check whether it was available or not.
  537.         BlockMove( *driver, driverPtr, driverSize );
  538.         
  539.         err = TradInstallDriverFromPtr(driverPtr, beginningUnit, endingUnit, refNum);
  540.     }
  541.     
  542.     // Clean up.
  543.     if (err != noErr) {
  544.         // We're returning an error.  The API says we should leave the handle untouched,
  545.         //  but we should definitely clean up our new copy of the driver code.
  546.         if (driverPtr != nil) {
  547.             DisposePtr( (Ptr) driverPtr );
  548.         }
  549.     }
  550.     
  551.     return (err);
  552. }
  553.  
  554. ///////////////////////////////////////////////////////////////////////////
  555.  
  556. extern pascal OSErr TradInstallDriverFromResource(SInt16 rsrcID, StringPtr rsrcName,
  557.                                                 UnitNumber beginningUnit,
  558.                                                 UnitNumber endingUnit,
  559.                                                 DriverRefNum *refNum)
  560.     // See comment in header file.
  561. {
  562.     OSStatus err;
  563.     Handle driverHandle;
  564.     
  565.     // Note: We don't care which zone the resource gets loaded, because 
  566.     //  TradInstallDriverFromHandle makes a copy of it anyway.
  567.  
  568.     // Get the resource, using either rsrcID or rsrcName.
  569.     if (rsrcName == nil) {
  570.         driverHandle = Get1Resource('DRVR', rsrcID);
  571.     } else {
  572.         driverHandle = Get1NamedResource('DRVR', rsrcName);
  573.     }
  574.     
  575.     // Set err if we couldn't get the resource.
  576.     if (driverHandle == nil) {
  577.         err = ResError();
  578.         if (err == noErr) {
  579.             err = resNotFound;
  580.         }
  581.     } else {
  582.         // Make sure we're not killed by some clown making the 'DRVR' purgeable.
  583.         HNoPurge(driverHandle);                    
  584.         err = MemError();
  585.     }
  586.     
  587.     // Now install the driver as if we'd got it from a memory handle.    
  588.     if (err == noErr) {
  589.         err = TradInstallDriverFromHandle( (DRVRHeaderHandle) driverHandle, beginningUnit, endingUnit, refNum);
  590.  
  591.         ReleaseResource(driverHandle);
  592.         if (err == noErr) {
  593.             err = ResError();
  594.         }
  595.     }
  596.     
  597.     return (err);
  598. }
  599.  
  600. ///////////////////////////////////////////////////////////////////////////
  601.  
  602. extern pascal OSErr TradGetDriverInformation(DriverRefNum refNum,
  603.                                                 UnitNumber *thisUnit,
  604.                                                 DriverFlags *flags,
  605.                                                 StringPtr name,
  606.                                                 DRVRHeaderPtr *driverHeader
  607.                                                 )
  608.     // See comment in header file.
  609. {
  610.     OSErr err;
  611.     UnitNumber             tmpUnit;
  612.     AuxDCEHandle        tmpDCE;
  613.     DRVRHeaderPtr        tmpHeader;
  614.     DRVRHeaderHandle    tmpDriverHandle;
  615.     
  616.     // Get some initial information.
  617.     tmpUnit = ~refNum;
  618.     
  619.     // Sanity check the refNum parameter.
  620.     err = noErr;
  621.     if (tmpUnit > TradHighestUnitNumber()) {
  622.         err = badUnitErr;
  623.     }
  624.     if (err == noErr) {
  625.         tmpDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  626.         if ( tmpDCE == nil ) {
  627.             err = unitEmptyErr;
  628.         }
  629.     }
  630.     if (err == noErr) {
  631.         if ( (*tmpDCE == nil) || (GetHandleSize( (Handle) tmpDCE) < sizeof(DCtlEntry)) ) {
  632.             err = dceExtErr;
  633.         }
  634.     }
  635.     
  636.     // Get the information from the DCE.
  637.     if (err == noErr) {
  638.  
  639.         // From the DCE, find the DRVR header.  This can fail for a number of reasons:
  640.         //     1. dCtlDriver is nil
  641.         //     2. the driver is handle based, and the handle's master point is nil
  642.         //     3. the driver is handle based, and the driver's handle is too small
  643.         // In all of these cases, we set tmpHeader to nil, returning nil to our
  644.         // client.
  645.         
  646.         tmpHeader = (DRVRHeaderPtr) (**tmpDCE).dCtlDriver;
  647.         if ( tmpHeader != nil ) {
  648.             if ( ((**tmpDCE).dCtlFlags & dRAMBasedMask) != 0 ) {
  649.  
  650.                 tmpDriverHandle = (DRVRHeaderHandle) tmpHeader;
  651.                 
  652.                 if ( (*tmpDriverHandle != nil) &&
  653.                             (GetHandleSize( (Handle) tmpDriverHandle) >= sizeof(DRVRHeader)) ) {
  654.                     tmpHeader = *tmpDriverHandle;
  655.                 }
  656.             }
  657.         }
  658.         
  659.         // Now copy out the various requested parameters
  660.         if (thisUnit != nil) {
  661.             *thisUnit = tmpUnit;
  662.         }
  663.         if (flags != nil) {
  664.             *flags = (**tmpDCE).dCtlFlags;
  665.         }
  666.         if (name != nil) {
  667.             if ( tmpHeader == nil ) {
  668.                 name[0] = 0;
  669.             } else {
  670.                 BlockMoveData(&tmpHeader->drvrName[0], name, tmpHeader->drvrName[0] + 1);
  671.             }
  672.         }
  673.         if (driverHeader != nil) {
  674.             *driverHeader = tmpHeader;
  675.         }
  676.     }
  677.     
  678.     return (err);
  679. }
  680.  
  681. ///////////////////////////////////////////////////////////////////////////
  682.  
  683. extern pascal OSErr TradRemoveDriver(DriverRefNum refNum, Boolean immediate)
  684.     // See comment in header file.
  685. {
  686.     OSErr                 err;
  687.     DriverFlags     flags;
  688.     DRVRHeaderPtr driverHeader;
  689.  
  690.     // Check parameters.
  691.     err = noErr;
  692.     if (immediate) {
  693.         err = paramErr;
  694.     }
  695.  
  696.     // Get information about the driver we're closing.
  697.     if (err == noErr) {
  698.         err = TradGetDriverInformation(refNum, nil, &flags, nil, &driverHeader);
  699.     }
  700.     if (err == noErr) {
  701.         if ( driverHeader == nil ) {
  702.             err = paramErr;
  703.         }
  704.     }
  705.     
  706.     // If the driver is open, close it.
  707.     if (err == noErr) {
  708.         if ( (flags & dOpenedMask) != 0 ) {
  709.             err = CloseDriver(refNum);
  710.         }
  711.     }
  712.     
  713.     // Now call the system to remove the driver from the unit table.  Note that this
  714.     //  works because of a subtlety in DriverRemove.  If the driver being removed
  715.     //  is a RAM-based driver (which our drivers aren't), DriverRemove will call
  716.     //  ReleaseResource on the dCtlDriver.  We don't want this, so we make our drivers
  717.     //  not RAM-based.
  718.     
  719.     if (err == noErr) {
  720.         err = DriverRemove(refNum);
  721.     }
  722.     
  723.     if (err == noErr) {
  724.         // All is cool, so let's dispose of the code.
  725.         DisposePtr( (Ptr) driverHeader);
  726.     }
  727.     
  728.     return (err);
  729. }
  730.  
  731. ///////////////////////////////////////////////////////////////////////////
  732.  
  733. extern pascal OSErr TradRenameDriver(DriverRefNum refNum, ConstStr255Param newDriverName)
  734.     // See *important* comment in header file.
  735. {
  736.     OSErr             err;
  737.     Str255             driverName;
  738.     DRVRHeaderPtr     driverHeader;
  739.     
  740.     err = noErr;
  741.     if ( newDriverName[0] == 0 ) {
  742.         err = paramErr;
  743.     }
  744.     if (err == noErr) {
  745.         // Get information about the driver we're renaming.
  746.         err = TradGetDriverInformation(refNum, nil, nil, driverName, &driverHeader);
  747.     }
  748.     if (err == noErr) {
  749.         if ( driverHeader == nil ) {
  750.             err = paramErr;
  751.         }
  752.     }
  753.     
  754.     // Now check the name lengths.  See comment in implementation for details.
  755.     if (err == noErr) {
  756.         if ( newDriverName[0] > driverName[0] ) {
  757.             err = paramErr;
  758.         }
  759.     }
  760.     
  761.     // Now check whether the new name is already present in the unit table.
  762.     if (err == noErr) {
  763.         if ( IsDriverInstalled(newDriverName, ~refNum) != kNoUnitNumber ) {
  764.             err = dupFNErr;
  765.         }
  766.     }
  767.     
  768.     // Now copy in the new driver name.
  769.     if (err == noErr) {
  770.         BlockMoveData( newDriverName, &driverHeader->drvrName[0], newDriverName[0] + 1 );
  771.     }
  772.     
  773.     return (err);
  774. }
  775.